package api

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

type router struct {
	roots        map[string]*node
	handlers     map[string]HandlerFunc
	middlewares  map[string]middlewareFunc
	errorHandler errorHandlerFn
}
type errorHandlerFn func(c *Context, err error)

func newRouter() *router {
	return &router{
		roots:       make(map[string]*node),
		handlers:    make(map[string]HandlerFunc),
		middlewares: make(map[string]middlewareFunc),
	}
}
func parsePattern(pattern string) []string {
	vs := strings.Split(pattern, "/")
	parts := make([]string, 0)
	for _, item := range vs {
		if item != "" {
			parts = append(parts, item)
			if item[0] == '*' {
				break
			}
		}
	}
	return parts
}

func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
	parts := parsePattern(pattern)
	key := method + "-" + pattern
	_, ok := r.roots[method]
	if !ok {
		r.roots[method] = &node{}
	}
	r.roots[method].insert(pattern, parts, 0)
	r.handlers[key] = handler
}
func (r *router) addMiddleware(method string, pattern string, handler middlewareFunc) {
	parts := parsePattern(pattern)
	key := method + "-" + pattern
	_, ok := r.roots[method]
	if !ok {
		r.roots[method] = &node{}
	}
	r.roots[method].insert(pattern, parts, 0)
	r.middlewares[key] = handler
}
func (r *router) getRoute(method string, path string) (*node, map[string]string) {
	searchParts := parsePattern(path)
	params := make(map[string]string)
	root, ok := r.roots[method]

	if !ok {
		return nil, nil
	}

	n := root.search(searchParts, 0)

	if n != nil {
		parts := parsePattern(n.pattern)
		for index, part := range parts {
			if part[0] == ':' {
				params[part[1:]] = searchParts[index]
			}
			if part[0] == '*' && len(part) > 1 {
				params[part[1:]] = strings.Join(searchParts[index:], "/")
				break
			}
		}
		return n, params
	}

	return nil, nil
}

func (r *router) handle(c *Context) {
	if r.errorHandler != nil {
		defer func() {
			if rec := recover(); rec != nil {
				r.errorHandler(c, errors.New(fmt.Sprintf("%s", rec)))
			}
		}()
	}

	for i := 0; i < middlewareBefore; i++ {
		v1, _ := r.getRoute(MiddlewareBefore+"-"+strconv.Itoa(i+1), c.Path)
		if v1 != nil {
			next := r.middlewares[MiddlewareBefore+"-"+strconv.Itoa(i+1)+"-"+v1.pattern](c)
			if !next {
				return
			}
		}
	}
	n, params := r.getRoute(c.Method, c.Path)
	if n != nil {
		c.Params = params
		key := c.Method + "-" + n.pattern
		r.handlers[key](c)
	} else {
		c.String("404 NOT FOUND: %s\n", c.Path)
	}
	for i := 0; i < middlewareAfter; i++ {
		v2, _ := r.getRoute(MiddlewareAfter+"-"+strconv.Itoa(i+1), c.Path)
		if v2 != nil {
			next := r.middlewares[MiddlewareAfter+"-"+strconv.Itoa(i+1)+"-"+v2.pattern](c)
			if !next {
				return
			}
		}
	}

}
